import { MathUtils } from "../../api/math/MathUtils";
import { HashMap } from "../../api/struct/HashMap";
import { E_DropActivityTag } from "../defs/drop/E_DropActivityTag";
import { E_DropFromType } from "../defs/item/E_DropFromType";
import { E_ItemID } from "../defs/item/E_ItemID";
import { E_ItemResolveType } from "../defs/item/E_ItemResolveType";
import { E_ItemType } from "../defs/item/E_ItemType";
import { E_PrayEffectType } from "../defs/lord/E_PrayEffectType";
import { CDActivity } from "../excel/CDActivity";
import { CDDrop } from "../excel/CDDrop";
import { CDDropGroup } from "../excel/CDDropGroup";
import { CDItem } from "../excel/CDItem";
import { CEDrop } from "../excel_extend/CEDrop";
import { CEDropSet } from "../excel_extend/CEDropSet";
import { DropContent } from "../struct/DropContent";
import { Player } from "../struct/Player";
import { SDActivity } from "./SDActivity";
import { SDEquip } from "./SDEquip";
import { SDHuangXiaoxi } from "./SDHuangXiaoxi";
import { SDPet } from "./SDPet";
import { SDPray } from "./SDPray";


/**
 * 掉落配置
 */
export interface DropConfig {
    /**预掉落 : false*/
    readonly preGenerate?: boolean;
    /**背包满就丢弃 : false*/
    readonly destroyWhenBagFull?: boolean;
}

/**掉落可丢弃 */
export const CAN_DESTROY_DROP_CONFIG = { destroyWhenBagFull: true };

/**
 * 数据-掉落
 */
export class SDDrop {

    /**各类型唯一性id （可能性掉落时，是各个的配置id）*/
    public uniqIds: number[][] = null;
    /**当前掉落信息-可叠加显示的道具 */
    public itemMap = new HashMap<number, number>();
    /**背包中的物品邮件id 0-没有邮件道具（客户端解析才有值） */
    public inBagMailId = 0;

    /**可能性掉落 */
    public possibleDrops: HashMap<number, any>[] = null;


    private constructor() {
        const idList: number[][] = this.uniqIds = [];
        const possible: HashMap<number, any>[] = this.possibleDrops = [];
        for (let i = 0, len = E_ItemType.EnumCount; i < len; i++) {
            idList[i] = [];
            possible[i] = new HashMap<number, any>();
        }
    }

    /**
     * 释放
     */
    public destroy(): void {
        this.reset();
        if (SDDrop.POOL.indexOf(this) === -1) {
            SDDrop.POOL.push(this);
        }
    }


    /**
     * 获取唯一性id长度
     * @returns 
     */
    public getUniqIDLength(): number {
        const list = this.uniqIds;
        let count = 0;
        for (let i = 0, len = list.length; i < len; i++) {
            count += list[i].length;
        }
        return count;
    }


    /**
     * 获取掉落总数量
     */
    public getDropCount(): number {
        const idLen = this.getUniqIDLength();
        const itemSize = this.itemMap.size();
        return idLen + itemSize;
    }


    /**
     * 重置
     */
    private reset(): void {

        //id
        {
            const idList = this.uniqIds;
            for (let i = 0, len = idList.length; i < len; i++) {
                idList[i].length = 0;
            }
        }

        //临时掉落
        {
            for (let i = 0, len = E_ItemType.EnumCount; i < len; i++) {
                const map = this.possibleDrops[i];
                const vs = map.getValues();
                for (let j = vs.length - 1; j >= 0; j--) {
                    if (vs[j].destroy) {
                        vs[j].destroy();
                    }
                }
                map.clear();
            }
        }

        //其他道具
        this.itemMap.clear();

        this.inBagMailId = 0;
    }


    /**
     * 添加可能掉落
     * @param player 
     * @param dropId 
     * @returns 
     */
    public addPossibleDrop(player: Player, dropId: number | number[]): this {
        if (dropId) {
            const configs: CDDrop[] = [];
            if (typeof (dropId) === 'number') {
                CEDrop.getPossibleDropConfigList(player, dropId, configs);
            }
            else {
                for (let i = 0, len = dropId.length; i < len; i++) {
                    CEDrop.getPossibleDropConfigList(player, dropId[i], configs);
                }
            }
            SDDrop.processPossibleDrop(player, configs, this);
        }
        return this;
    }


    /**
     * 使用掉落数据初始化
     * @param drop 
     */
    public initByDropString(drop: string, reset = false): this {
        const jsonv = JSON.parse(drop);
        const ids: number[][] = jsonv[0];
        const ks: number[] = jsonv[1];
        const vs: number[] = jsonv[2];
        const mailId: number = jsonv[3];

        if (reset) {
            this.reset();
        }

        //唯一性id
        if (ids) {
            const list = this.uniqIds;
            if (ids.length !== list.length) {
                console.error('掉落数据的长度异常! len = ' + ids.length);
            }
            for (let i = 0, len = list.length; i < len; i++) {
                const dst = list[i];
                const src = ids[i];
                for (let j = 0, l2 = src.length; j < l2; j++) {
                    dst.push(src[j]);
                }
            }
        }

        //可叠加道具
        if (vs.length > 0) {
            for (let i = 0, len = vs.length; i < len; i++) {
                const oldv = this.itemMap.get(ks[i]) || 0;
                this.itemMap.put(ks[i], oldv + vs[i]);
            }
        }

        this.inBagMailId = mailId;

        return this;
    }


    /**
    * 计算掉落加成
    * @param player 
    * @param fromType 
    * @returns 
    */
    public processBuffAdded(
        player: Player,
        fromType = E_DropFromType.Common
    ): void {

        if (!player) {
            return;
        }

        const itemMap = this.itemMap;
        const ks = itemMap.getKeys();
        const vs = itemMap.getValues();

        //祝福增加的经验掉落
        if (fromType !== E_DropFromType.Mail) {
            let exp = itemMap.get(E_ItemID.Exp);
            if (0 !== exp) {
                const percent = SDPray.getEffectByType(player, E_PrayEffectType.Exp);
                if (0 !== percent) {
                    exp += Math.floor(exp * percent / 100);
                    itemMap.put(E_ItemID.Exp, exp);
                }
            }
        }
        //黄小西增加的经验掉落
        {
            let exp = itemMap.get(E_ItemID.Exp);
            if (0 !== exp) {
                const percent = SDHuangXiaoxi.getEffectByType(player, E_PrayEffectType.Exp);
                if (0 !== percent) {
                    exp += Math.floor(exp * percent / 100);
                    itemMap.put(E_ItemID.Exp, exp);
                }
            }
        }

        //挂机铜钱加成
        if (fromType === E_DropFromType.Hangup) {
            let copper = itemMap.get(E_ItemID.Copper);
            if (0 !== copper) {
                const percent = SDPray.getEffectByType(player, E_PrayEffectType.CopperProduce);
                if (0 !== percent) {
                    copper += Math.floor(copper * percent / 100);
                    itemMap.put(E_ItemID.Copper, copper);
                }
            }
        }
        //黄小西增加的铜钱掉落
        {
            let copper = itemMap.get(E_ItemID.Copper);
            if (0 !== copper) {
                const percent = SDHuangXiaoxi.getEffectByType(player, E_PrayEffectType.CopperProduce);
                if (0 !== percent) {
                    copper += Math.floor(copper * percent / 100);
                    itemMap.put(E_ItemID.Copper, copper);
                }
            }
        }

        //熔炼晶石增加
        if (fromType === E_DropFromType.Smelt) {
            const percent = SDPray.getEffectByType(player, E_PrayEffectType.SmeltAdd);
            if (0 !== percent) {
                for (let i = 0, len = ks.length; i < len; i++) {
                    const config = CDItem.getData(ks[i]);
                    if (!config || config.resolveType !== E_ItemResolveType.CrystalStone) {
                        continue;
                    }
                    vs[i] += Math.floor(vs[i] * percent / 100);
                }
            }
            //黄小西增加的熔炼晶石掉落
            {
                const percent = SDHuangXiaoxi.getEffectByType(player, E_PrayEffectType.SmeltAdd);
                if (0 !== percent) {
                    for (let i = 0, len = ks.length; i < len; i++) {
                        const config = CDItem.getData(ks[i]);
                        if (!config || config.resolveType !== E_ItemResolveType.CrystalStone) {
                            continue;
                        }
                        vs[i] += Math.floor(vs[i] * percent / 100);
                    }
                }
            }
        }

        //广告增加钻石
        if (fromType === E_DropFromType.AD) {
            let diamonds = itemMap.get(E_ItemID.Diamonds);
            if (0 !== diamonds) {
                const percent = SDPray.getEffectByType(player, E_PrayEffectType.ADDiamondsAdd);
                if (0 !== percent) {
                    diamonds += Math.floor(diamonds * percent / 100);
                    itemMap.put(E_ItemID.Diamonds, diamonds);
                }
                //黄小西增加的钻石掉落
                const percent2 = SDHuangXiaoxi.getEffectByType(player, E_PrayEffectType.ADDiamondsAdd);
                if (0 !== percent2) {
                    diamonds += Math.floor(diamonds * percent2 / 100);
                    itemMap.put(E_ItemID.Diamonds, diamonds);
                }

            }
        }
    }


    /**
     * src的内容添加到dst上
     * @param dst 
     * @param src 
     */
    public static moveDropContent(dst: DropContent, src: DropContent): void {
        //物品
        {
            const ks = src.getItemKeys();
            const vs = src.getItemValues();
            for (let i = ks.length - 1; i >= 0; i--) {
                const ov = dst.getItem(ks[i]) || 0;
                dst.putItem(ks[i], ov + vs[i]);
            }
        }

        //装备
        {
            const list = src.getEquipValues();
            for (let i = 0, len = list.length; i < len; i++) {
                dst.putEquip(list[i].equipId, list[i].clone());
            }
        }

        //宠物
        {
            const list = src.getPetValues();
            for (let i = 0, len = list.length; i < len; i++) {
                dst.putPet(list[i].petId, list[i].clone());
            }
        }
    }


    /**
     * 添加掉落内容
     * @param player
     * @param dc 
     * @param destroyDropContent 
     * @param exceptItem 不拷贝消耗品
     */
    public addFromDropContent(
        player: Player,
        dc: DropContent,
        destroyDropContent = false,
        exceptItem = false
    ): this {
        if (!dc || !player) {
            return this;
        }

        //处理
        {
            //物品
            if (!exceptItem)
            {
                const ks = dc.getItemKeys();
                const vs = dc.getItemValues();
                for (let i = ks.length - 1; i >= 0; i--) {
                    const ov = this.itemMap.get(ks[i]) || 0;
                    this.itemMap.put(ks[i], ov + vs[i]);
                }
            }

            //装备
            {
                const list = dc.getEquipValues();
                const ids = this.uniqIds[E_ItemType.Equip];
                for (let i = 0, len = list.length; i < len; i++) {
                    const ele = list[i];
                    ids.push(ele.equipId);
                    player.equipManager.putEquip(ele.equipId, ele.clone());
                }
            }

            //宠物
            {
                const list = dc.getPetValues();
                const ids = this.uniqIds[E_ItemType.Pet];
                for (let i = 0, len = list.length; i < len; i++) {
                    const ele = list[i];
                    ids.push(ele.petId);
                    player.petManager.putPet(ele.petId, ele.clone());
                }
            }
        }

        //释放
        if (destroyDropContent) {
            dc.destroy();
        }

        return this;
    }


    /**
    * 拷贝掉落内容到掉落
    * @param player 
    * @param dropContent 
    */
    public copyToDropContent(
        player: Player,
        dropContent = DropContent.create()
    ): DropContent {

        //给物品
        {
            const itemMap = this.itemMap;
            const ks = itemMap.getKeys();
            const vs = itemMap.getValues();
            for (let i = ks.length - 1; i >= 0; i--) {
                const ov = dropContent.getItem(ks[i]) || 0;
                dropContent.putItem(ks[i], ov + vs[i]);
            }
        }

        //预处理的要移除
        if (!player) {
            return dropContent;
        }

        const idList = this.uniqIds;
        for (let i = 0, len = idList.length; i < len; i++) {
            const list = idList[i];
            switch (i) {
                case E_ItemType.Equip:
                    {
                        const em = player.equipManager;
                        for (let j = list.length - 1; j >= 0; j--) {
                            const equip = em.removeEquip(list[j]);
                            if (equip) {
                                dropContent.putEquip(equip.equipId, equip);
                            }
                        }
                    }
                    break;

                case E_ItemType.Pet:
                    {
                        const pm = player.petManager;
                        for (let j = list.length - 1; j >= 0; j--) {
                            const pet = pm.removePet(list[j]);
                            if (pet) {
                                dropContent.putPet(pet.petId, pet);
                            }
                        }
                    }
                    break;

                default:
                    if (list.length > 0) {
                        console.error('SDDrop not process pre generate, item type = ' + i);
                    }
                    break;
            }
        }

        return dropContent;
    }


    //---------------------------------------------------------------


    /**缓存池 */
    private static readonly POOL: SDDrop[] = [];

    /**
      * 创建
      * @returns 
      */
    public static create(): SDDrop {
        const drop = this.POOL.pop() || new SDDrop();
        drop.reset();
        return drop;
    }


    /**
     * 处理可能掉落
     * @param dropList 
     * @param drop 
     */
    public static processPossibleDrop(
        player: Player,
        dropList: CDDrop[],
        drop: SDDrop
    ): void {

        if (!dropList) {
            return;
        }

        for (let i = 0, len = dropList.length; i < len; i++) {
            const dropConfig = dropList[i];
            const num = this.getDropNum(player, dropConfig);
            if (dropConfig.itemType < E_ItemType.EnumCount) {
                this.addPossibleDropCell(drop, dropConfig.itemType, dropConfig.itemid, num, dropConfig.equipmentLevel);
                continue;
            }

            //目前特殊标记为99 drop-set
            const lv = dropConfig.equipmentLevel;
            const sets = CEDropSet.getData(dropConfig.itemid);

            //TODO set config未指定item type，目前默认是装备
            for (let n = 0; n < num; n++) {
                const random = MathUtils.random(0, 10000);
                for (let i = 0, len = sets.length; i < len; i++) {
                    const setConfig = sets[i];
                    if (setConfig.rateLow <= random && random <= setConfig.rateUp) {
                        this.addPossibleDropCell(drop, setConfig.itemType, setConfig.itemid, 1, lv);
                    }
                }
            }
        }
    }


    /**
     * 可能性掉落
     * @param drop 
     * @param itemType 
     * @param itemId 
     * @param num 
     * @param equipLevel 
     * @returns 
     */
    private static addPossibleDropCell(
        drop: SDDrop,
        itemType: E_ItemType,
        itemId: number,
        num: number,
        equipLevel: number
    ): void {

        if (itemType <= 0 || itemType >= E_ItemType.EnumCount) {
            console.warn('item type = ' + itemType + ' in drop config is undefined');
            return;
        }

        //唯一性id的掉落显示一个
        const list = drop.uniqIds[itemType];
        if (list.indexOf(itemId) >= 0) {
            return;
        }

        const map = drop.possibleDrops[itemType];
        const oldSize = map.size();

        switch (itemType) {
            case E_ItemType.Equip:
                {
                    const equip = SDEquip.createNew(null, itemId, equipLevel);
                    if (equip) {
                        map.put(equip.equipId, equip);
                    }
                }
                break;

            case E_ItemType.Pet:
                {
                    const pet = SDPet.createNew(null, itemId);
                    if (pet) {
                        map.put(pet.petId, pet);
                    }
                }
                break;

            //TODO 非默认类型的处理

            default:
                if (num > 0) {
                    if (this.isNeedCreateUniq(itemType)) {
                        console.error('未处理需要单独id生成的掉落：item type = ' + itemType);
                    }
                    const oldNum = drop.itemMap.get(itemId) || 0;
                    if (oldNum > num || oldNum === 0) {
                        drop.itemMap.put(itemId, num);      //可能掉落存数量少的
                    }
                }
                break;
        }

        //保存 4 过滤重复
        if (oldSize !== map.size()) {
            list.push(itemId);
        }
    }


    /**
    * 是否需要创建唯一性id
    * @param itemType 
    * @returns 
    */
    public static isNeedCreateUniq(itemType: E_ItemType): boolean {
        if (itemType === E_ItemType.Pet
            || itemType === E_ItemType.Equip) {
            return true;
        }
        return false;
    }


    /**
     * 掉落内容是否为空
     * @param dc 
     */
    public static isDropContentEmpty(dc: DropContent): boolean {
        if (dc.getItemKeys().length > 0
            || dc.getEquipKeys().length > 0
            || dc.getPetKeys().length > 0) {
            return false;
        }
        return true;
    }


    /**
     * 添加掉落信息-道具
     * @param itemId 
     * @param num 
     * @param drop
     */
    public static addDropInfoItem(
        itemId: number,
        num: number,
        drop: SDDrop
    ): void {
        if (0 !== num) {
            const oldNum = drop.itemMap.get(itemId) || 0;
            drop.itemMap.put(itemId, oldNum + num);
        }
    }


    /**
     * 获取掉落数量
     * @param player 
     * @param config 
     * @returns 
     */
    public static getDropNum(
        player: Player,
        config: CDDrop
    ): number {

        let num = config.num;

        //活动增加掉落数量
        if (num > 0 && config.activityTag === E_DropActivityTag.AddNum) {
            const groupConfig = CDDropGroup.getData(config.dropid);
            if (groupConfig
                && groupConfig.activityid > 0
                && SDActivity.isOpen(player, groupConfig.activityid)) {

                const activityConfig = CDActivity.getData(groupConfig.activityid);
                if (activityConfig) {
                    num = Math.floor(num * activityConfig.doubleParam / 100);
                }
            }
        }

        return num;
    }



}
